<?php
/**
 *
 */
namespace core;
class Loader {
    /**
     * @var array 命名空间别名
     */
    protected static $namespaceAlias = [];

    /**
     * @var array 实例数组
     */
    protected static $instance = [];

    /**
     * @var array 类名映射
     */
    protected static $classMap = [];

    /**
     * @var array PSR-4 命名空间前缀长度映射
     */
    private static $prefixLengthsPsr4 = [];

    /**
     * @var array PSR-4 的加载目录
     */
    private static $prefixDirsPsr4 = [];
    
    /**
     * @var array PSR-4 加载失败的回退目录
     */
    private static $fallbackDirsPsr4 = [];
    /**
     * @var array PSR-0 命名空间前缀映射
     */
    private static $prefixesPsr0 = [];

    /**
     * @var array PSR-0 加载失败的回退目录
     */
    private static $fallbackDirsPsr0 = [];


    function __construct() {
        //Todo
    }

    /**
     * @param null $autoload
     * 自动加载
     */
    public static function register($autoload = null) {
        // 注册系统自动加载（核心目录Loader的autoload方法）
        spl_autoload_register($autoload ?: '\core\\Loader::autoload', true, true);
        // 注册命名空间定义（注册核心目录命名空间）
        self::addNamespace([
            'core' => ROOT_PATH . 'core' . DS,
        ]);
    }

    /**
     * 初始化类的实例
     * @access public
     * @return void
     */
    public static function clearInstance()
    {
        self::$instance = [];
    }

    /**
     * 自动加载
     * @access public
     * @param  string $class 类名
     * @return bool
     */
    public static function autoload($class) {
        // 检测命名空间别名
        if (!empty(self::$namespaceAlias)) {
            $namespace = dirname($class);
            if (isset(self::$namespaceAlias[$namespace])) {
                $original = self::$namespaceAlias[$namespace] . '\\' . basename($class);
                if (class_exists($original)) {
                    return class_alias($original, $class, false);
                }
            }
        }
        //查找类文件是否存在
        if ($file = self::findFile($class)) {
            // 非 Win 环境不严格区分大小写
            if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
                //引入文件
                __include_file($file);
                return true;
            }
        }

        return false;
    }

    /**
     * 查找文件
     * @access private
     * @param  string $class 类名
     * @return bool|string
     */
    private static function findFile($class) {

        // 类库映射
        if (!empty(self::$classMap[$class])) {
            return self::$classMap[$class];
        }

        // 查找 PSR-4（将命名空间转换为路径【core\Config转换为core/Config.php】）
        $logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
        $first = $class[0];

        if (isset(self::$prefixLengthsPsr4[$first])) {
            foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
                if (0 === strpos($class, $prefix)) {
                    foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
                        if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
                            return $file;
                        }
                    }
                }
            }
        }

        // 查找 PSR-4 fallback dirs
        foreach (self::$fallbackDirsPsr4 as $dir) {
            if (is_file($file = $dir . DS . $logicalPathPsr4)) {
                return $file;
            }
        }

        // 查找 PSR-0
        if (false !== $pos = strrpos($class, '\\')) {
            // namespace class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_', DS) . EXT;
        }
        if (isset(self::$prefixesPsr0[$first])) {
            foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (is_file($file = $dir . DS . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }
        // 查找 PSR-0 fallback dirs
        foreach (self::$fallbackDirsPsr0 as $dir) {
            if (is_file($file = $dir . DS . $logicalPathPsr0)) {
                return $file;
            }
        }
        // 找不到则设置映射为 false 并返回
        return self::$classMap[$class] = false;
    }

    /**
     * 注册命名空间
     * @access public
     * @param  string|array $namespace 命名空间
     * @param  string $path 路径
     * @return void
     */
    public static function addNamespace($namespace, $path = '') {
        if (is_array($namespace)) {
            foreach ($namespace as $prefix => $paths) {
                //添加 PSR-4 空间【core\, __ROOT__/core】
                self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
            }
        } else {
            //添加 PSR-4 空间
            self::addPsr4($namespace . '\\', rtrim($path, DS), true);
        }
    }

    /**
     * 添加 PSR-4 空间
     * 把命名空间映射添加到命名空间里【array(1) { ["core\"]=> array(1) { [0]=> "__ROOT__/core" } }】
     * @access private
     * @param  array|string $prefix 空间前缀
     * @param  string $paths 路径
     * @param  bool $prepend 预先设置的优先级更高
     * @return void
     */
    private static function addPsr4($prefix, $paths, $prepend = false) {
        if (!$prefix) {
            // Register directories for the root namespace.
            self::$fallbackDirsPsr4 = $prepend ?
                array_merge((array)$paths, self::$fallbackDirsPsr4) :
                array_merge(self::$fallbackDirsPsr4, (array)$paths);

        } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException(
                    "A non-empty PSR-4 prefix must end with a namespace separator."
                );
            }

            self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            self::$prefixDirsPsr4[$prefix] = (array)$paths;

        } else {
            self::$prefixDirsPsr4[$prefix] = $prepend ?
                // Prepend directories for an already registered namespace.
                array_merge((array)$paths, self::$prefixDirsPsr4[$prefix]) :
                // Append directories for an already registered namespace.
                array_merge(self::$prefixDirsPsr4[$prefix], (array)$paths);

        }
    }

    /**
     * 字符串命名风格转换
     * type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格
     * @access public
     * @param  string $name 字符串
     * @param  integer $type 转换类型
     * @param  bool $ucfirst 首字母是否大写（驼峰规则）
     * @return string
     */
    public static function parseName($name, $type = 0, $ucfirst = true) {
        if ($type) {
            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
                return strtoupper($match[1]);
            }, $name);

            return $ucfirst ? ucfirst($name) : lcfirst($name);
        }

        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
    }

    /**
     * 实例化（分层）控制器 格式：[模块名/]控制器名
     * @access public
     * @param  string $name         资源地址
     * @param  string $layer        控制层名称
     * @param  bool   $appendSuffix 是否添加类名后缀
     * @param  string $empty        空控制器名称
     * @return object
     * @throws ClassNotFoundException
     */
    public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
    {
        list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
        if (class_exists($class)) {
            return App::invokeClass($class);
        }

        if ($empty) {
            $emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);

            if (class_exists($emptyClass)) {
                return new $emptyClass(Request::instance());
            }
        }

    }

    /**
     * 解析模块和类名
     * @access protected
     * @param  string $name         资源地址
     * @param  string $layer        验证层名称
     * @param  bool   $appendSuffix 是否添加类名后缀
     * @return array
     */
    protected static function getModuleAndClass($name, $layer, $appendSuffix)
    {

        if (false !== strpos($name, '\\')) {
            $module = Request::instance()->module();
            $class  = $name;

        } else {
            if (strpos($name, '/')) {
                list($module, $name) = explode('/', $name, 2);
            } else {
                $module = Request::instance()->module();
            }
            $class = self::parseClass($module, $layer, $name, $appendSuffix);
        }
        return [$module, $class];
    }

    /**
     * 解析应用类的类名
     * @access public
     * @param  string $module       模块名
     * @param  string $layer        层名 controller model ...
     * @param  string $name         类名
     * @param  bool   $appendSuffix 是否添加类名后缀
     * @return string
     */
    public static function parseClass($module, $layer, $name, $appendSuffix = false)
    {

        $array = explode('\\', str_replace(['/', '.'], '\\', $name));
        $class = self::parseName(array_pop($array), 1);
        $class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
        $path  = $array ? implode('\\', $array) . '\\' : '';
        return App::$namespace . '\\' .
        ($module ? $module . '\\' : '') .
        $layer . '\\' . $path . $class;
    }

}

/**
 * 引入文件
 * @param  string $file 文件路径
 * @return mixed
 */
function __include_file($file) {
    return include $file;
}